package com.xyh.transaction.service.imp;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.xyh.transaction.common.Constant;
import com.xyh.transaction.common.Current;
import com.xyh.transaction.common.ImageType;
import com.xyh.transaction.common.UserRole;
import com.xyh.transaction.entity.Image;
import com.xyh.transaction.entity.LoginUser;
import com.xyh.transaction.entity.Role;
import com.xyh.transaction.entity.User;
import com.xyh.transaction.entity.dto.user.*;
import com.xyh.transaction.entity.vo.GenderVo;
import com.xyh.transaction.entity.vo.ReturnUserVo;
import com.xyh.transaction.entity.vo.StatusVo;
import com.xyh.transaction.entity.vo.UserVo;
import com.xyh.transaction.excel.ExportExcelUserVo;
import com.xyh.transaction.excel.ImportExcelUserVo;
import com.xyh.transaction.excel.ImportUserVoListener;
import com.xyh.transaction.exception.BusinessException;
import com.xyh.transaction.exception.DuplicateUsernameException;
import com.xyh.transaction.exception.InsertException;
import com.xyh.transaction.listener.MapAvatarImageEvent;
import com.xyh.transaction.mapper.ImageMapper;
import com.xyh.transaction.mapper.RoleMapper;
import com.xyh.transaction.mapper.UserMapper;
import com.xyh.transaction.service.UserService;
import com.xyh.transaction.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationListener;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@Transactional
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    private ImageMapper imageMapper;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private ApplicationEventPublisher eventListener;

    @Value("${jwt.header}")
    private String tokenHeader;


    @Override
    public User findByNameAndPassword(String name, String password) {

        return userMapper.findByNameAndPassword(name, password);
    }

    @Override
    public ReturnUserVo login(User user) {

        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());

        Authentication authenticate = authenticationManager.authenticate(authenticationToken);

        //认证通过，生成jwt
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        Integer userId = loginUser.getUser().getId();
        String username = loginUser.getUser().getUsername();
        String token = jwtUtil.generateJwtToken(username, userId.toString());

        // 保存用户信息到redis
        String key = String.format("login_%s_%s", userId, username);
        // 设置过期时间是jwt的两倍,为实现token无感刷新
        redisUtil.set(key, loginUser, Constant.JWT_EXPIRE_TIME * 2, TimeUnit.MILLISECONDS);

        log.info("用户 {} 登录成功", loginUser.getUser().getUsername());

        ReturnUserVo returnUserVo = new ReturnUserVo();
        returnUserVo.setHeader(tokenHeader);
        returnUserVo.setToken(token);

        UserVo userVo = MappingUtil.mapObject(loginUser.getUser(), UserVo.class);
        eventListener.publishEvent(new MapAvatarImageEvent(loginUser.getUser(), userVo));
        Set<String> roleNames = roleMapper.findRoleByUserId(userId)
                .stream()
                .map(Role::getRoleName)
                .collect(Collectors.toSet());
        userVo.setRoleNames(roleNames);
        returnUserVo.setUserInfo(userVo);

        return returnUserVo;
    }

    @Override
    public void register(EditOrAddDto addUser) {
        User user = MappingUtil.mapObject(addUser, User.class);
        // 去空格
        String username = user.getUsername().trim();
        user.setUsername(username);

        User byName = userMapper.findByName(username);
        if (byName != null) {
            throw new DuplicateUsernameException(username);
        }

        user.setStatus(1);// 默认启用
        user.setCreateTime(LocalDateTime.now());

        // 设置默认密码12345
        user.setPassword(passwordEncoder.encode(Constant.DEFAULT_PASSWORD));
        int userId = userMapper.insert(user);

        // 保存头像
        imageMapper.deleteByRelatedId(userId, ImageType.AVATAR);
        mappingAvatarImage(addUser.getAvatar(), username, userId);

        // 添加角色,默认买家
        if (addUser.getRoleNames() != null && !addUser.getRoleNames().isEmpty()) {
            addUser.getRoleNames()
                    .forEach(roleName -> userMapper.insertRole(user.getId(), UserRole.getByDetail(roleName).getCode()));
        } else {
            userMapper.insertRole(user.getId(), UserRole.Buyer.getCode());
        }

    }

    @Override
    public void logout() {
        String id = Current.getCurrentUser().getUser().getId().toString();
        String username = Current.getCurrentUser().getUser().getUsername();

        String key = "login_" + id + "_" + username;

        // 删除redis中的登录信息
        redisUtil.delete(key);
    }

    @Override
    public PageInfo<User> list(UserPageDto userPageDto) {

        Integer pageNum = userPageDto.getPageNum();
        Integer pageSize = userPageDto.getPageSize();

        pageNum = pageNum == null || pageNum <= 0 ? 1 : pageNum;
        pageSize = pageSize == null || pageSize <= 0 ? 10 : pageSize; // 默认显示10条

        // 在查询语句执行之前调用 PageHelper.startPage
        PageHelper.startPage(pageNum, pageSize);
        // 将 userPageDto 转为 SearchDto
        SearchDto searchDto = MappingUtil.mapObject(userPageDto, SearchDto.class);
        // 查询数据库
        List<User> userList = userMapper.findAll(searchDto);
        // 隐藏密码
        List<User> data = userList.stream()
                .peek(user -> user.setPassword(null))
                .collect(Collectors.toList());
        List<UserVo> userVoList = data.stream().map(user -> {
            UserVo userVo = MappingUtil.mapObject(user, UserVo.class);
            eventListener.publishEvent(new MapAvatarImageEvent(user, userVo));
            return userVo;
        }).collect(Collectors.toList());
        userVoList.forEach(userVo -> {
            List<String> roleNames = roleMapper.findRoleByUserId(userVo.getId())
                    .stream()
                    .sorted(Comparator.comparing(Role::getId))
                    .map(Role::getRoleName)
                    .collect(Collectors.toList());
            userVo.setRoleNames(roleNames);
        });
//        // 将 User 转为 UserVo
//        List<UserVo> userVoList = userList.stream()
//                .map(user -> {
//                    UserVo userVo = new UserVo();
//                    userVo.setId(user.getId());
//                    userVo.setAddress(user.getAddress());
//                    userVo.setAge(user.getAge());
//                    userVo.setAvatar(user.getAvatar());
//                    userVo.setCreateTime(user.getCreateTime());
//                    userVo.setEmail(user.getEmail());
//                    userVo.setGender(user.getGender());
//                    userVo.setUsername(user.getUsername());
//                    userVo.setStatus(user.getStatus());
//                    userVo.setIdCard(user.getIdCard());
//                    return userVo;
//                })
//                .collect(Collectors.toList());

//         使用 PageInfo.of(list) 创建包含其他参数的 PageInfo 对象
        PageInfo pageInfo = PageInfo.of(userList);
        // 将列表数据设置为转换后的 VO 对象列表
        pageInfo.setList(userVoList);

        return pageInfo;
    }


    @Override
    public ResponseEntity<byte[]> exportUserList(SearchDto searchDto) {
        List<User> userList = userMapper.findAll(searchDto);
        // 映射为ExportExcelUserVo对象
        List<ExportExcelUserVo> exportExcelUserVos = userList.stream().map(user -> {
            ExportExcelUserVo excelUserVo = MappingUtil.mapObject(user, ExportExcelUserVo.class);

            UserVo userVo = new UserVo();
            eventListener.publishEvent(new MapAvatarImageEvent(user, userVo));
            try {
                byte[] imageBytes = ImageUtils.downloadImage(userVo.getAvatar()); // 使用新的图片下载方法
                excelUserVo.setAvatar(imageBytes);
            } catch (IOException e) {
                log.error("用户头像读取失败！", e);
                throw new RuntimeException("用户头像读取失败！", e);
            }

            return excelUserVo;
        }).collect(Collectors.toList());
        // 使用 ExcelUtil 进行导出
        return ExcelUtil.exportToExcel(
                exportExcelUserVos,
                "userList.xlsx",
                ExportExcelUserVo.class);
    }

    @Override
    public Set<GenderVo> getGender() {
        List<User> userList = userMapper.findAll(new SearchDto());

        return userList.stream().map(user -> {
            Integer gender = user.getGender();
            GenderVo genderVo = new GenderVo();
            if (gender.equals(0)) {
                genderVo.setGenderLabel("女");
                genderVo.setGenderValue(gender);
            } else if (gender.equals(1)) {
                genderVo.setGenderLabel("男");
                genderVo.setGenderValue(gender);
            }
            return genderVo;
        }).collect(Collectors.toSet());
    }

    @Override
    public Set<StatusVo> getStatus() {
        List<User> userList = userMapper.findAll(new SearchDto());

        return userList.stream().map(user -> {
            StatusVo statusVo = new StatusVo();
            Integer status = user.getStatus();
            if (status.equals(0)) {
                statusVo.setUserLabel("禁用");
                statusVo.setUserStatus(status);
            } else if (status.equals(1)) {
                statusVo.setUserLabel("启用");
                statusVo.setUserStatus(status);
            }
            return statusVo;
        }).collect(Collectors.toSet());
    }

    @Override
    public void changeStatus(ChangeDto changeDto) {
        User user = MappingUtil.mapObject(changeDto, User.class);
        userMapper.updateUserById(user);
    }

    @Override
    public void restPassword(RestPwdDto restPwdDto) {
        User user = MappingUtil.mapObject(restPwdDto, User.class);
        user.setPassword(passwordEncoder.encode(Constant.DEFAULT_PASSWORD));
        userMapper.updateUserById(user);
    }

    @Override
    public void delete(ArrDto arrDto) {
        if (arrDto.getId().length != 0) {
            userMapper.updateStatus(arrDto.getId());// 逻辑删除
        }
        // 删除用户和角色关联关系
//        userMapper.deleteRoleByUserId(ArrDto.getId());
//        userMapper.deleteBatch(ArrDto.getId());
    }

    @Override
    public void updateUser(EditOrAddDto editOrAddDto) {
        User user = MappingUtil.mapObject(editOrAddDto, User.class);
        userMapper.updateUserById(user);
        Integer userId = editOrAddDto.getId();
        Set<String> roleNames = editOrAddDto.getRoleNames();
        // 用户原来拥有的角色列表
        Set<Role> roleList = roleMapper.findRoleByUserId(userId);
        // 如果用户角色列表没有发生变化，直接返回
        if (roleList.stream().map(Role::getRoleName).equals(roleNames)) {
            return;
        }
        // 修改的角色列表
        List<Integer> roleIdList = roleMapper.findByNames(roleNames).stream()
                .map(Role::getId)
                .collect(Collectors.toList());
        if (!roleList.isEmpty()) {
            userMapper.deleteRoleByUserIds(new Integer[]{userId});
        }
        // 保存头像
        imageMapper.deleteByRelatedId(userId, ImageType.AVATAR);
        mappingAvatarImage(editOrAddDto.getAvatar(), user.getUsername(), user.getId());
        roleIdList.forEach(roleId -> {
            Integer i = userMapper.insertRole(userId, roleId);
            if (i <= 0) {
                throw new InsertException("为用户添加角色失败！");
            }
        });
    }

    private void mappingAvatarImage(String url, String username, Integer userId) {
        // 把图片保存进t_image
        Image image = new Image();
        image.setAltText(username);
        image.setUrl(ImageUtils.getFileNameFromUrl(url));
        image.setRelatedId(userId);
        image.setImageType(ImageType.AVATAR);
        image.setCreateTime(LocalDateTime.now());
        image.setUpdateTime(LocalDateTime.now());
        imageMapper.insert(image);
    }

    @Override
    public void saveUser(MultipartFile file, Boolean isCover) {
        try (InputStream inputStream = file.getInputStream()) {
            ExcelUtil.readExcel(
                    inputStream,
                    ImportExcelUserVo.class,
                    new ImportUserVoListener(isCover, userMapper, passwordEncoder));
        } catch (Exception e) {
            log.error("批量添加用户异常：{}", e.getMessage(), e);
            throw new BusinessException("批量添加用户异常！", e);
        }

    }

    @Override
    public List<Integer> getRouteIdList(Integer userId) {
        roleMapper.findRoleByUserId(userId);

        return null;
    }

    @Override
    public void setPassword(User user) {
        Integer userId = Current.getCurrentUser().getUser().getId();
        // 加密密码
        String password = passwordEncoder.encode(user.getPassword());
        user.setPassword(password);
        user.setId(userId);

        userMapper.updateUserById(user);

        // 更新密码之后，退出登录
        logout();
    }

    @Override
    public void addUserRole(Integer id, Set<String> roleNames) {
        Set<Role> byNames = roleMapper.findByNames(roleNames);
        roleMapper.findByNames(roleNames).forEach(role -> userMapper.insertRole(id, role.getId()));
    }
}
